package com.aaron.server;

import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.xml.serialize.DOMSerializer;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.Serializer;
import org.apache.xml.serialize.SerializerFactory;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class SubversionServer {
	public SubversionServer()throws DatabaseException{
		DAVRepositoryFactory.setup(); //for http(s) connections
		SVNRepositoryFactoryImpl.setup(); // for svn and svn+ssh connections.
		FSRepositoryFactory.setup();
	}

	public String getTreeXML(String name, String path)throws Exception {
		try{
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document document = builder.newDocument(); // Create from whole cloth
			Element root=parseDirectoryToXML(new File(path).getCanonicalFile(), document);
			//rename root element's name to something more aesthetic 
			root.setAttribute("name", name);
			document.appendChild(root);
	
			// transform DOM to String
			StringWriter out = new StringWriter();
			
			SerializerFactory serializerFactory=SerializerFactory.getSerializerFactory("xml");
			if(serializerFactory==null){
				throw new Exception("serializerFactory is null");
			}
			OutputFormat format=new OutputFormat(document, "utf-8", true);
			Serializer serializer=serializerFactory.makeSerializer(out, format);
			DOMSerializer domSerializer=serializer.asDOMSerializer();
			domSerializer.serialize(document);
			return out.getBuffer().toString();
		}
		catch(ParserConfigurationException e){
			return "";
		}
	}

	public static Element parseDirectoryToXML(File parent, Document document) throws SVNException, Exception {
		if(parent==null){
			throw new Exception("Parent is null");
		}
		if(!parent.exists()){
			throw new Exception("Parent does not exist: "+parent.getPath());
		}
		if(!parent.isDirectory()){
			throw new Exception("Parent is not a directory: "+parent.getPath());
		}
		if(!parent.canRead()){
			throw new Exception("Cannot read parent directory");
		}
		Element folder_element = document.createElement("Folder");
		
		folder_element.setAttribute("name", parent.getName());
		folder_element.setAttribute("path", parent.getPath());
		File[] entries = parent.listFiles();
		for (int i=0; i<entries.length; i++) {
			File child=entries[i];
			if (child.isDirectory() && !child.isHidden()) {
				folder_element.appendChild(parseDirectoryToXML(child, document));
			} else if (child.isFile() && !child.isHidden()) {
				Element file_element = document.createElement("File");
				file_element.setAttribute("name", child.getName());
				file_element.setAttribute("path", child.getPath());
				
				folder_element.appendChild(file_element);
			}
		}
		return folder_element;
	}
	
	public void update(String svn_url, String svn_username, String svn_password)
			throws Exception {
		ISVNAuthenticationManager authManager = login(svn_username, svn_password);
		ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
		SVNClientManager clientManager = SVNClientManager.newInstance(options,
				authManager);
		SVNUpdateClient updateClient = clientManager.getUpdateClient();
		updateClient.setIgnoreExternals(false);
		File wcDir = new File(getPath(getUUID(svn_username, svn_url)));
		wcDir=wcDir.getCanonicalFile();
        if ( !wcDir.exists() ) {
        	System.err.println ( "the source directory '"
                    + wcDir.getAbsolutePath( ) + "' does not exists!" );
        }
		updateClient.doUpdate(wcDir, SVNRevision.HEAD, true);
	}

	public void commit(String svn_url, String message, String svn_username, String svn_password)
			throws Exception {
		ISVNAuthenticationManager authManager = login(svn_username, svn_password);
		ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
		SVNClientManager clientManager = SVNClientManager.newInstance(options,
				authManager);
		SVNUpdateClient updateClient = clientManager.getUpdateClient();
		updateClient.setIgnoreExternals(false);
		File wcDir = new File(getPath(getUUID(svn_username, svn_url)));
		wcDir=wcDir.getCanonicalFile();
        if ( !wcDir.exists() ) {
        	System.err.println ( "the source directory '"
                    + wcDir.getAbsolutePath( ) + "' does not exists!" );
        }
		clientManager.getCommitClient().doCommit( new File[] { wcDir } , true , message , false , true );
	}

	public void checkout(String name, String svn_url, String username, String svn_username, String svn_password)throws Exception{
		try{
			ISVNAuthenticationManager authManager = login(svn_username, svn_password);
			ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
			SVNClientManager clientManager = SVNClientManager.newInstance(options,
					authManager);
			SVNUpdateClient updateClient = clientManager.getUpdateClient();
			updateClient.setIgnoreExternals(false);
	        File wcDir = new File(getPath(getUUID(svn_username, svn_url)));
	        wcDir=wcDir.getCanonicalFile();
	        if ( wcDir.exists() ) {
	        	System.err.println ( "the destination directory '"
	                    + wcDir.getAbsolutePath( ) + "' already exists!" );
	        }
	        FileUtils.forceMkdir(wcDir);
	        //if(!wcDir.mkdirs( )){
	        	//throw new Exception("Cannot make directory"+wcDir.getAbsolutePath());
	        //}
	        System.err.println ("SVN destination directory:"+wcDir.getAbsolutePath());
			updateClient.doCheckout(SVNURL.parseURIEncoded(svn_url), wcDir, SVNRevision.HEAD,
					SVNRevision.HEAD, true);
			
			//if everything has been checked out, insert the Project into the Projects database
			ProjectServer project_server=new ProjectServer();
			project_server.createProject(name, getUUID(svn_username, svn_url), username, svn_url, svn_username, svn_password);
		}
		catch(IllegalArgumentException e){
			 System.err.println("checkout:"+e.getMessage()+e);
			 StackTraceElement[] stack=e.getStackTrace();
			 for(int i=0; i<stack.length; i++){
				 System.err.println(stack[i].getClassName()+"."+stack[i].getMethodName()+" at "+stack[i].getFileName()+":"+stack[i].getLineNumber());
			 }
		}
		catch(SVNException e){
			System.err.println("checkout: SVNException: "+e.getMessage()+e.getErrorMessage().getFullMessage());
			throw e;
		}
		catch(SecurityException e){
			System.err.println("checkout: SecurityException: "+e.getMessage());
			throw e;
		}
		catch(IOException e){
			System.err.println("checkout: IOException: "+e.getMessage());
			throw e;
		}
		catch(DatabaseException e){
			System.err.println("checkout: DatabaseException: "+e.getMessage());
			throw e;
		}
	}
	public void modifyFile(String svn_url, String svn_username, String svn_password, String file_path, String contents) throws SVNException {
		//ISVNEditor editor , String dirPath , String filePath , byte[] oldData , ;
		ISVNAuthenticationManager authManager = login(svn_username, svn_password);
		ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
		SVNClientManager clientManager = SVNClientManager.newInstance(options,
				authManager);
		SVNDiffClient diffClient=clientManager.getDiffClient();
		
		File original = new File(file_path);
		try{
			//create a temporary file to hold the contents of the submitted changes
			File update= File.createTempFile("vivcode", "tmp");
			FileOutputStream fis = new FileOutputStream(update);
			DataOutputStream  dis = new DataOutputStream(fis);
			BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(dis));
			//write the submitted changes to the temporary file
			bw.write(contents);
		   
			
			//	File path1, SVNRevision revision1, File path2, SVNRevision revision2, File dstPath,
			diffClient.doMerge(original, SVNRevision.WORKING, update, SVNRevision.WORKING, original, false, false, true, false);
		}
		catch(IOException e){
			System.err.println("modifyFile: IOException: "+e.getMessage());
		}

    }

	private static ISVNAuthenticationManager login(String svn_username,
			String svn_password)  {
		ISVNAuthenticationManager authManager = SVNWCUtil
				.createDefaultAuthenticationManager(svn_username, svn_password);
		return authManager;
	}
	public static String getUUID(String svn_username, String svn_url){
		//This ought to be  unique to each user, project combination
		return DigestUtils.shaHex(svn_username+svn_url);
	}
	public static String getPath(String uuid){
		return "project-cache/"+uuid;
	}
}
